home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-11-15 | 28.1 KB | 960 lines | [TEXT/CWIE] |
- // QuickDraw 3d Sample Code
- //
- // This file contains utility routines for QuickDraw 3d sample code.
- // Shows how to read a metafile and render it.
- //
- // Created 27th Dec 1994, Nick Thompson, DEVSUPPORT
-
-
- #include <Files.h>
- #include <QuickDraw.h>
- #include <QDOffScreen.h>
- #include <StandardFile.h>
- #include <TextUtils.h>
- #include <Strings.h>
-
- #include "CustomAttributesSupport.h"
- #include "CustomAttributesShell.h"
-
- #include <QD3D.h>
- #include <QD3DDrawContext.h>
- #include <QD3DRenderer.h>
- #include <QD3DShader.h>
- #include <QD3DCamera.h>
- #include <QD3DLight.h>
- #include <QD3DGeometry.h>
- #include <QD3DTransform.h>
- #include <QD3DGroup.h>
- #include <QD3DMath.h>
-
- #include <QD3DStorage.h>
- #include <QD3DIO.h>
- #include <QD3DString.h>
-
- #include "CustomAttribute_Lib.h"
-
- //-----------------------------------------------------------------------------------------------
- // local utility functions
- static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText ) ;
-
- void GetGroupBBox(
- DocumentPtr theDocument,
- TQ3BoundingBox *viewBBox) ;
-
- static TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ;
-
- static TQ3Status GetDocumentGroupBoundingBox(
- DocumentPtr theDocument ,
- TQ3BoundingBox *viewBBox) ;
-
- //-----------------------------------------------------------------------------------------------
- // Submit the scene for rendering/fileIO and picking
- TQ3Status SubmitScene( DocumentPtr theDocument )
- {
- Q3Style_Submit(theDocument->fInterpolation, theDocument->fView);
- Q3Style_Submit(theDocument->fBackFacing , theDocument->fView);
- Q3Style_Submit(theDocument->fFillStyle, theDocument->fView);
-
- Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView);
-
- Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView);
-
- return kQ3Success ;
- }
-
- //-----------------------------------------------------------------------------------------------
-
- static TQ3Status GetDocumentGroupBoundingBox(
- DocumentPtr theDocument ,
- TQ3BoundingBox *viewBBox)
- {
- TQ3Status status;
- TQ3ViewStatus viewStatus ;
-
- status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate );
- do {
- status = SubmitScene( theDocument ) ;
- } while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse );
- return status ;
- }
-
- //-----------------------------------------------------------------------------------------------
-
- TQ3ViewObject MyNewView(WindowPtr theWindow)
- {
- TQ3Status myStatus;
- TQ3ViewObject myView;
- TQ3DrawContextObject myDrawContext;
- TQ3RendererObject myRenderer;
- TQ3CameraObject myCamera;
- TQ3GroupObject myLights;
-
- myView = Q3View_New();
-
- // Create and set draw context.
- if ((myDrawContext = MyNewDrawContext(theWindow)) == nil )
- goto bail;
-
- if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
- goto bail;
-
- Q3Object_Dispose( myDrawContext ) ;
-
- // Create and set renderer.
- //
- // hacky way to do this, but since I wanted these snippets to have
- // a minimal interface, this will suffice
- //
- // change the next line to “#if 1” to use the WF renderer
-
- #if 0
- // this would use the wireframe renderer
- myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame);
- if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
- goto bail;
- }
- #else
- // this would use the interactive software renderer
-
- if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != nil ) {
- if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
- goto bail;
- }
- }
- else {
- goto bail;
- }
- #endif
-
- Q3Object_Dispose( myRenderer ) ;
-
- // Create and set camera.
- if ( (myCamera = MyNewCamera(theWindow)) == nil )
- goto bail;
-
- if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
- goto bail;
-
- Q3Object_Dispose( myCamera ) ;
-
- // Create and set lights.
- if ((myLights = MyNewLights()) == nil )
- goto bail;
-
- if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
- goto bail;
-
- Q3Object_Dispose(myLights);
-
- // Done!!!
- return ( myView );
-
- bail:
- // If any of the above failed, then don't return a view.
- return ( nil );
- }
-
- //----------------------------------------------------------------------------------
-
- TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow)
- {
- TQ3DrawContextData myDrawContextData;
- TQ3MacDrawContextData myMacDrawContextData;
- TQ3ColorARGB ClearColor;
- TQ3DrawContextObject myDrawContext ;
-
- ClearColor.a = 1.0;
- ClearColor.r = 1.0;
- ClearColor.g = 1.0;
- ClearColor.b = 1.0;
-
- // Fill in draw context data.
- myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
- myDrawContextData.clearImageColor = ClearColor;
-
- myDrawContextData.paneState = kQ3False;
- myDrawContextData.maskState = kQ3False;
-
- myDrawContextData.doubleBufferState = kQ3True;
-
- myMacDrawContextData.drawContextData = myDrawContextData;
-
- myMacDrawContextData.window = (CGrafPtr) theWindow; // this is the window associated with the view
- myMacDrawContextData.library = kQ3Mac2DLibraryNone;
- myMacDrawContextData.viewPort = nil;
- myMacDrawContextData.grafPort = nil;
-
- // Create draw context and return it, if it’s nil the caller must handle
- myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ;
-
- return myDrawContext ;
- }
-
- //----------------------------------------------------------------------------------
-
- TQ3CameraObject MyNewCamera(WindowPtr theWindow)
- {
- TQ3CameraObject myCamera;
- TQ3CameraData myCameraData;
- TQ3ViewAngleAspectCameraData myViewAngleCameraData;
- TQ3Point3D cameraFrom = { 0.0, 0.0, 1.5 };
- TQ3Point3D cameraTo = { 0.0, 0.0, 0.0 };
- TQ3Vector3D cameraUp = { 0.0, 1.0, 0.0 };
-
- float hither = 0.4;
- float yon = 3.6;
-
- /*
- we are going to set this up so that we have a view volume that measures 2x2x2
- and is centered on 0,0,0. In this demo, the default units are in meters, so
- that's a 2 meter box.
- */
-
- // Fill in camera data.
- myCameraData.placement.cameraLocation = cameraFrom;
- myCameraData.placement.pointOfInterest = cameraTo;
- myCameraData.placement.upVector = cameraUp;
-
- myCameraData.range.hither = hither;
- myCameraData.range.yon = yon;
-
- myCameraData.viewPort.origin.x = -1.0;
- myCameraData.viewPort.origin.y = 1.0;
- myCameraData.viewPort.width = 2.0;
- myCameraData.viewPort.height = 2.0;
-
- myViewAngleCameraData.cameraData = myCameraData;
-
- // the half with is 1 at a distance of 1.5
- myViewAngleCameraData.fov = 2 * atan( 1 / 1.5) ;
-
- // set up the aspect ratio based on the window
- myViewAngleCameraData.aspectRatioXToY =
- (float) (theWindow->portRect.right - theWindow->portRect.left) /
- (float) (theWindow->portRect.bottom - theWindow->portRect.top);
-
- myCamera = Q3ViewAngleAspectCamera_New(&myViewAngleCameraData);
-
- // Return the camera.
- return ( myCamera );
- }
-
-
- //----------------------------------------------------------------------------------
-
- TQ3GroupObject MyNewLights()
- {
- TQ3GroupPosition myGroupPosition;
- TQ3GroupObject myLightList;
- TQ3LightData myLightData;
- TQ3PointLightData myPointLightData;
- TQ3DirectionalLightData myDirectionalLightData;
- TQ3LightObject myAmbientLight, myPointLight, myFillLight;
- TQ3Point3D pointLocation = { -10.0, 0.0, 10.0 };
- TQ3Vector3D fillDirection = { 10.0, 0.0, 10.0 };
- TQ3ColorRGB WhiteLight = { 1.0, 1.0, 1.0 };
-
- // Set up light data for ambient light. This light data will be used for point and fill
- // light also.
-
- myLightData.isOn = kQ3True;
- myLightData.color = WhiteLight;
-
- // Create ambient light.
- myLightData.brightness = .2;
- myAmbientLight = Q3AmbientLight_New(&myLightData);
- if ( myAmbientLight == nil )
- goto bail;
-
- // Create point light.
- myLightData.brightness = 1.0;
- myPointLightData.lightData = myLightData;
- myPointLightData.castsShadows = kQ3False;
- myPointLightData.attenuation = kQ3AttenuationTypeNone;
- myPointLightData.location = pointLocation;
- myPointLight = Q3PointLight_New(&myPointLightData);
- if ( myPointLight == nil )
- goto bail;
-
- // Create fill light.
- myLightData.brightness = .2;
- myDirectionalLightData.lightData = myLightData;
- myDirectionalLightData.castsShadows = kQ3False;
- myDirectionalLightData.direction = fillDirection;
- myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
- if ( myFillLight == nil )
- goto bail;
-
- // Create light group and add each of the lights into the group.
- myLightList = Q3LightGroup_New();
- if ( myLightList == nil )
- goto bail;
- myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
- if ( myGroupPosition == 0 )
- goto bail;
- myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
- if ( myGroupPosition == 0 )
- goto bail;
- myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
- if ( myGroupPosition == 0 )
- goto bail;
-
- Q3Object_Dispose( myAmbientLight ) ;
- Q3Object_Dispose( myPointLight ) ;
- Q3Object_Dispose( myFillLight ) ;
-
- // Done!
- return ( myLightList );
-
- bail:
- // If any of the above failed, then return nothing!
- return ( nil );
- }
-
- //----------------------------------------------------------------------------------
-
- TQ3GroupObject MyNewModelFromFile(FSSpec *theFileSpec, DocumentPtr theDocument)
- {
- TQ3GroupObject mainGroup = NULL, modelGroup = NULL;
- TQ3Boolean isText = kQ3False ;
- TQ3FileMode myFileMode = 0;
- TQ3FileObject theFile;
-
- // Create a ordered group for all the elements that make up the group.
- if ((mainGroup = Q3OrderedDisplayGroup_New()) == NULL )
- return NULL;
-
- MyAddShaderToGroup( mainGroup ) ;
-
- // Create a display group for the things that we read from the file.
- if ((modelGroup = Q3DisplayGroup_New()) == NULL )
- return NULL;
-
- // Add the model group to the document's group
-
- Q3Group_AddObject(mainGroup, modelGroup);
-
- theFile = MyGetNewFile( theFileSpec, &isText ) ;
-
- if( isText == kQ3True )
- myFileMode |= kQ3FileModeText; // is it a text metafile??
-
- // Open the file object
- if( Q3File_OpenRead( theFile, &myFileMode ) != kQ3Success)
- return NULL ;
-
- theDocument->fModel = mainGroup;
-
- if( MyReadModelFromFile( theFile, modelGroup, theDocument ) == kQ3Failure)
- DebugStr("\pMetafile data read is null") ;
-
- Q3File_Close(theFile); // close and dispose of the file object
- Q3Object_Dispose(theFile);
-
-
- return mainGroup ;
- }
-
-
- //----------------------------------------------------------------------------------
- // attach a shader to the group
-
- TQ3Status MyAddShaderToGroup( TQ3GroupObject group )
- {
- TQ3ShaderObject illuminationShader = Q3PhongIllumination_New();
-
- Q3Group_AddObject(group, illuminationShader);
- Q3Object_Dispose(illuminationShader);
- return(kQ3Success);
- }
-
- //----------------------------------------------------------------------------------
- // read model from file object into the supplied group
-
- TQ3Status MyReadModelFromFile( TQ3FileObject theFile,TQ3GroupObject myGroup, DocumentPtr theDocument)
- {
- TQ3Boolean alreadyGotAttributes = kQ3False;
-
- if(theFile != NULL) {
-
- TQ3Object myTempObj ;
- TQ3Boolean isEOF ;
-
-
- // read objects from the file
- do {
-
- myTempObj = Q3File_ReadObject( theFile );
-
- if( myTempObj != NULL ) {
- // we only want the object in our main group if we can draw it
- if ( Q3Object_IsDrawable( myTempObj) ) {
-
- if( alreadyGotAttributes == kQ3False) {
- TQ3SetObject set = NULL, groupSet = NULL;
-
- /*
- It only makes sense to interpret attributes like scale, up vector, and front vector on a file
- as a whole. So, once we find one, that's the one we are going to use.
- */
- if( Q3Object_IsType(myTempObj, kQ3ShapeTypeGeometry) == kQ3True) {
- Q3Geometry_GetAttributeSet(myTempObj, &set);
- }
-
- if(set == NULL && Q3Object_IsType(myTempObj, kQ3SharedTypeShape) == kQ3True){
- Q3Shape_GetSet(myTempObj, &set);
- }
-
- // we are going to migrate the attributes up
- Q3Shape_GetSet(theDocument->fModel, &groupSet);
- if( groupSet == NULL) {
- groupSet = Q3Set_New();
- if( groupSet ) {
- Q3Shape_SetSet(theDocument->fModel, groupSet);
- }
- }
-
- if( set ) {
- if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
- double scale;
-
- Q3Set_Get(set, kElementTypeScale, &scale);
-
- alreadyGotAttributes = kQ3True;
-
- Q3Shape_GetSet(theDocument->fModel, &groupSet);
-
- if( groupSet )
- Q3Set_Add(groupSet, kElementTypeScale, &scale);
- }
-
- if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
- TQ3Vector3D upVector;
-
- Q3Set_Get(set, kElementTypeUpVector, &upVector);
-
- alreadyGotAttributes = kQ3True;
-
- if( groupSet )
- Q3Set_Add(groupSet, kElementTypeUpVector, &upVector);
- }
-
- if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
- TQ3Vector3D forwardVector;
-
- Q3Set_Get(set, kElementTypeForwardDirection, &forwardVector);
-
- alreadyGotAttributes = kQ3True;
-
- if( groupSet )
- Q3Set_Add(groupSet, kElementTypeForwardDirection, &forwardVector);
- }
-
- Q3Object_Dispose(set);
-
- // balances both the get or the new
- if( groupSet )
- Q3Object_Dispose(groupSet);
- }
- }
-
- // Note that since myGroup is a display group, we preserve the order of the objects in the file
- Q3Group_AddObject( myGroup, myTempObj ) ;
- }
-
- // we either added the object to the main group, or we don't care
- // so we can safely dispose of the object
- Q3Object_Dispose( myTempObj ) ;
- }
-
- // check to see if we reached the end of file yet
- isEOF = Q3File_IsEndOfFile( theFile );
-
- } while (isEOF == kQ3False);
- }
-
- if( alreadyGotAttributes != kQ3True ) {
- StopAlert( NoScale_Alert, nil ) ;
- return kQ3Failure;
- }
-
- if( myGroup != NULL )
- return kQ3Success ;
- else
- return kQ3Failure ;
- }
-
- //-----------------------------------------------------------------------------------------------
- // cleaned up from IM QuickDraw 3D pp 15-5
- static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText )
- {
- TQ3FileObject myFileObj;
- TQ3StorageObject myStorageObj;
- OSType myFileType;
-
- FInfo fndrInfo ;
-
- // we assume the FSSpec passed in was valid, get the file information
- // we need to know the file type, this routine may get called by an appleEvent
- // handler, so we can't assume a type, we need to get it from the fsspec.
-
- FSpGetFInfo( myFSSpec, &fndrInfo ) ;
-
- // pull out the file type
-
- myFileType = fndrInfo.fdType ;
-
- // Create new storage object and new file object
- if(((myStorageObj = Q3FSSpecStorage_New( myFSSpec )) == NULL)
- || ((myFileObj = Q3File_New()) == NULL))
- {
- if (myStorageObj != NULL)
- Q3Object_Dispose(myStorageObj);
- return(NULL);
- }
-
- // Set the storage for the file object
- Q3File_SetStorage(myFileObj, myStorageObj);
- Q3Object_Dispose(myStorageObj);
-
- if (myFileType == '3DMF')
- *isText = kQ3False ;
- else if (myFileType == 'TEXT')
- *isText = kQ3True ;
-
- return (myFileObj);
- }
-
-
- //-------------------------------------------------------------------------------------------
- //
- Boolean MetafileFileSpecify( FSSpec *theFile )
- {
- StandardFileReply theSFReply ;
- SFTypeList myTypes = { '3DMF' } ;
- const short numTypes = 1 ;
-
- // Get the file name to open
- StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
-
- if( theSFReply.sfGood )
- *theFile = theSFReply.sfFile ;
-
- // did the user cancel?
- return theSFReply.sfGood ;
-
- }
- //----------------------------------------------------------------------------------
-
-
- void GetGroupBBox(
- DocumentPtr theDocument,
- TQ3BoundingBox *viewBBox)
- {
- TQ3Point3D from = { 0.0, 0.0, 1.0 };
- TQ3Point3D to = { 0.0, 0.0, 0.0 };
- TQ3Vector3D up = { 0.0, 1.0, 0.0 };
-
- float fieldOfView = .52359333333;
- float hither = 0.5;
- float yon = 1.5;
- TQ3GroupObject mainGroup = theDocument->fModel ;
-
- TQ3Status status;
-
- #ifdef BETA_1_BUILD
- Q3View_StartBounds( theDocument->fView );
-
- status = Q3DisplayGroup_BoundingBox(mainGroup,
- viewBBox,
- kQ3ComputeBoundsApproximate,
- viewObject);
-
- Q3View_EndBounds( theDocument->fView );
- #else
- status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ;
- #endif
-
- //
- // If we have a point model, then the "viewBBox" would end up
- // being a "singularity" at the location of the point. As
- // this bounding "box" is used in setting up the camera spec,
- // we get bogus input into Escher.
-
- {
- float xSize, ySize, zSize;
-
- xSize = viewBBox->max.x - viewBBox->min.x;
- ySize = viewBBox->max.y - viewBBox->min.y;
- zSize = viewBBox->max.z - viewBBox->min.z;
-
- if (xSize <= kQ3RealZero &&
- ySize <= kQ3RealZero &&
- zSize <= kQ3RealZero) {
-
- viewBBox->max.x += 0.0001;
- viewBBox->max.y += 0.0001;
- viewBBox->max.z += 0.0001;
-
- viewBBox->min.x -= 0.0001;
- viewBBox->min.y -= 0.0001;
- viewBBox->min.z -= 0.0001;
- }
- }
- }
-
-
-
-
- //------------------------------------------------------------------------
-
-
- void AdjustCamera(
- DocumentPtr theDocument,
- short winWidth,
- short winHeight)
- {
- TQ3BoundingBox viewBBox;
- TQ3CameraObject camera;
- TQ3GroupObject mainGroup = theDocument->fModel ;
- TQ3ViewObject theView = theDocument->fView;
- TQ3Vector3D objectFront;
-
- Q3View_GetCamera( theView, &camera);
- GetGroupBBox( theDocument, &viewBBox);
-
- if( viewBBox.isEmpty == kQ3False ) {
- float width,
- length,
- height,
- largestDimension;
-
- TQ3TransformObject scaleTransform = NULL,
- orientationTransform = NULL,
- centerTransform = NULL;
-
- TQ3SetObject set;
-
- double scale;
- TQ3Vector3D scaleTransformData={1.0, 1.0, 1.0};
- TQ3RotateAboutAxisTransformData rotateTransform={{0,0,0},{0,0,1},0};
- TQ3Matrix4x4 orientationMatrix = { 1.0, 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0,
- 0.0, 0.0, 0.0, 1.0};
- /*
- The ordered group, theDocument->fModel, is going to contain the following transforms
-
- first a scale transform to take care of the scale attribute
- second a matrix with the rotations to take care of the up and forward vectors info
- third a translate transform to position the object starting at 0 in z and centered in x and y
-
- remember that the transforms are applied in the reverse order, which is to say that the
- resulting point p' from point p is
-
- p' = scale * rotation * translate * p
- */
-
- // initialize the scale and orientation transforms
-
- // add an identify transform for the scale, values will be set later if needed
- scaleTransform = Q3ScaleTransform_New(&scaleTransformData);
- if( scaleTransform )
- Q3Group_AddObject(mainGroup, scaleTransform);
-
- // calculate the extents of the bounding box
- width = viewBBox.max.x - viewBBox.min.x;
- height = viewBBox.max.y - viewBBox.min.y;
- length = viewBBox.max.z - viewBBox.min.z;
-
- // add an identify transform for the orientation, values will be set later if needed
-
- // the object's center will be set at 0,0,-length/2 later
- rotateTransform.origin.x = 0.0;
- rotateTransform.origin.y = 0.0;
- rotateTransform.origin.z = - length * 0.5;
-
- orientationTransform = Q3MatrixTransform_New(&orientationMatrix);
- if( orientationTransform )
- Q3Group_AddObject(mainGroup, orientationTransform);
-
- // Now we get the custom elements for this model
- Q3Shape_GetSet(mainGroup, &set);
-
- if( set ) {
- // let's get the scale
- if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
- Q3Set_Get(set, kElementTypeScale, &scale);
- }
-
- // let's get the up vector
- if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
- TQ3Vector3D cameraUpVector = {0.0, 1.0, 0.0},
- upVector;
- TQ3TransformObject tempTransform;
-
- Q3Set_Get(set, kElementTypeUpVector, &upVector);
-
- Q3Vector3D_Normalize(&upVector, &upVector);
-
- // Get the axis for the rotation
- Q3Vector3D_Cross(&cameraUpVector, &upVector, &rotateTransform.orientation);
- // Check to see that the vectors are not co-linear
- // WARNING - the code is not dealing with opposing vectors (ie one vector point in
- // +X direction and the other in -X. The length will be zero, and so will the dot
- // product. You will have to generate an arbitrary axis for the 180° rotation.
- if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
- Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
- // Get the angle, only valid if vectors are unit length
- rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraUpVector, &upVector));
- tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
-
- Q3MatrixTransform_Set(orientationTransform,
- Q3Transform_GetMatrix(tempTransform, &orientationMatrix));
-
- Q3Object_Dispose(tempTransform);
- }
- }
-
- // let's get the forward vector
- if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
- TQ3Vector3D cameraViewVector = {0.0, 0.0, 1.0},
- forwardDirection;
- TQ3TransformObject tempTransform;
- TQ3Matrix4x4 matrixUp, matrixForward;
-
- Q3Set_Get(set, kElementTypeForwardDirection, &forwardDirection);
-
- Q3Vector3D_Normalize(&forwardDirection, &forwardDirection);
-
- // Get the axis for the rotation
- Q3Vector3D_Cross(&cameraViewVector, &forwardDirection, &rotateTransform.orientation);
- // Check to see that the vectors are not co-linear
- // WARNING - the code is not dealing with opposing vectors (ie one vector point in
- // +X direction and the other in -X. The length will be zero, and so will the dot
- // product. You will have to generate an arbitrary axis for the 180° rotation.
- if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
- Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
- // Get the angle, only valid if vectors are unit length
- rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraViewVector, &forwardDirection));
- tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
-
- Q3Transform_GetMatrix(tempTransform, &matrixForward);
-
- // concatenate with previous rotation, getting it from the matrix
- Q3Transform_GetMatrix(orientationTransform, &matrixUp);
-
- Q3MatrixTransform_Set(orientationTransform,
- Q3Matrix4x4_Multiply(&matrixUp, &matrixForward, &orientationMatrix));
-
- Q3Object_Dispose(tempTransform);
- }
- }
-
- Q3Object_Dispose(set);
- }
-
- // Now we'll deal with the scale
-
- largestDimension = width;
- if( largestDimension < height)
- largestDimension = height;
- if( largestDimension < length)
- largestDimension = length;
-
- largestDimension *= scale;
-
- if( largestDimension > theDocument->units ) {
- short itemHit ;
-
- itemHit = StopAlert( LargeObject_Alert, nil ) ;
-
- if( itemHit == 1 ){
- MenuHandle hMenu;
- hMenu = GetMenu(Settings_Menu);
-
- CheckItem(hMenu, Scale1M, false);
- CheckItem(hMenu, Scale10M, false);
- CheckItem(hMenu, Scale100M, false);
- CheckItem(hMenu, Scale1KM, false);
-
- if( largestDimension <= 10.0 ) {
- // the working units become 10 meters
- // scale the object down by 10
- if( largestDimension < 10.0)
- scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.1;
- Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
- theDocument->units = 10.0;
- CheckItem(hMenu, Scale10M, true);
- } else if( largestDimension <= 100.0 ) {
- // the working units become 100 meters
- // scale the object down by 100
- if( largestDimension < 100.0)
- scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.01;
- Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
- theDocument->units = 100.0;
- CheckItem(hMenu, Scale100M, true);
- } else if( largestDimension <= 1000.0 ) {
- // the working units become 1000 meters
- // scale the object down by 1000
- if( largestDimension < 1000.0)
- scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.001;
- Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
- theDocument->units = 1000.0;
- CheckItem(hMenu, Scale1KM, true);
- } else {
- itemHit = StopAlert( TooLargeObject_Alert, nil ) ;
- }
- }
- }
-
- // add a translate transform to center the object in the scene
-
- // move the object so that the front is at the view plane (Z direction) and centered in x and y
- objectFront.x = - (viewBBox.min.x + width * 0.5);
- objectFront.y = - (viewBBox.min.y + height * 0.5);
- // We are looking into negative z, that's why we use maximum
- objectFront.z = - viewBBox.max.z;
-
- centerTransform = Q3TranslateTransform_New(&objectFront);
- Q3Group_AddObject(mainGroup, centerTransform);
- Q3Object_Dispose(centerTransform);
-
- if( scaleTransform )
- Q3Object_Dispose(scaleTransform);
- }
-
- Q3ViewAngleAspectCamera_SetAspectRatio(
- camera, (float) winWidth / (float) winHeight);
-
- Q3Object_Dispose(camera);
-
- return;
- }
-
- // We use this function to do a quick check for attributes, to help us decide whether it's enough to look at
- // the geometry's attribute set level, or if we need to go up to the shape's set
-
- static TQ3Boolean CheckSetForAttributes(TQ3SetObject set)
- {
- if( Q3Set_Contains( set, kElementTypeName) == kQ3True )
- return kQ3True;
-
- if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True )
- return kQ3True;
-
- return kQ3False;
- }
-
- // This function gets the attributes, if they exists, and display them
- static TQ3Boolean FindAndDisplayCustomAttributes(DocumentPtr theDocument, TQ3Object object)
- {
- TQ3SetObject set = NULL;
- RGBColor color = {0,0,0};
-
- RGBForeColor(&color);
- TextMode(srcXor);
-
- if( Q3Object_IsType(object, kQ3ShapeTypeGeometry) == kQ3True) {
- Q3Geometry_GetAttributeSet(object, &set);
- if( set && CheckSetForAttributes(set) == kQ3False ) {
- /* Let's check at the shape set level */
- Q3Object_Dispose(set);
- set = NULL;
- }
- }
-
- if(set == NULL && Q3Object_IsType(object, kQ3SharedTypeShape) == kQ3True){
- Q3Shape_GetSet(object, &set);
- }
-
- if( set ) {
- if( Q3Set_Contains( set, kElementTypeName) == kQ3True ) {
- Str255 pascalString;
- TQ3StringObject string;
-
- Q3Set_Get( set, kElementTypeName, &string);
-
- if( theDocument->fStringObject != string ) {
- /* Delete previously displayed text */
- if( theDocument->fStringObject ) {
- Str255 pascalString;
-
- strcpy((char *) pascalString, theDocument->fName);
- MoveTo(10,10);
- DrawString(pascalString);
-
- Q3CString_EmptyData(&theDocument->fName);
- Q3Object_Dispose(theDocument->fStringObject);
- }
-
- theDocument->fStringObject = string;
-
- Q3CString_GetString(theDocument->fStringObject, &theDocument->fName);
-
- strcpy((char *) pascalString, theDocument->fName);
- MoveTo(10,10);
- DrawString(c2pstr((char *)pascalString));
- }
- }
-
- if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True ) {
- Str255 pascalString;
- W3AnchorData urlData;
-
- Q3Set_Get( set, kElementTypeW3Anchor, &urlData);
-
- if( theDocument->fDescription != urlData.description) {
- if( theDocument->fDescription ) {
- /* Delete previously displayed url */
- MoveTo(10,30);
- DrawString(theDocument->fURL);
-
- MoveTo(10,50);
- strcpy((char *) pascalString, theDocument->fDescString);
- DrawString(c2pstr((char *)pascalString));
-
- Q3CString_EmptyData(&theDocument->fDescString);
-
- Q3Object_Dispose(theDocument->fDescription);
- }
-
- MoveTo(10,30);
- strcpy((char *) theDocument->fURL, urlData.url);
- c2pstr((char *)theDocument->fURL);
- DrawString(theDocument->fURL);
-
- MoveTo(10,50);
- Q3CString_GetString(urlData.description, &theDocument->fDescString);
- strcpy((char *) pascalString, theDocument->fDescString);
- DrawString(c2pstr((char *)pascalString));
-
- theDocument->fDescription = urlData.description;
- }
-
- if( theDocument->launchNetscape == kQ3True ) {
- if( OpenURL(urlData.url) == false)
- StopAlert( Netscape_Alert, nil );
- }
- }
-
- Q3Object_Dispose(set);
- return( kQ3True );
- }
- return( kQ3False );
- }
-
- void DisplayCustomAttributes(DocumentPtr theDocument)
- {
- TQ3HitData hitData;
-
- Q3Pick_GetHitData(theDocument->fPickObject, 0, &hitData);
-
- if( hitData.object ) {
- FindAndDisplayCustomAttributes(theDocument, hitData.object);
- }
- Q3Pick_EmptyHitList(theDocument->fPickObject);
- Q3Hit_EmptyData(&hitData);
- }
-
-